<template>
  <div>
    <div class="native-class">
      是否允许调整菜单顺序：
      <el-switch
        style="margin-right: 15px"
        v-model="draggable"
        active-color="#13ce66"
        inactive-color="#ff4949"
      ></el-switch>
      <el-button
        v-if="draggable"
        type="success"
        plain
        size="mini"
        @click="batchUpdateSort()"
      >
        保存更改
      </el-button>
      <el-button
        v-if="draggable"
        type="danger"
        plain
        size="mini"
        @click="cancelUpdate()"
      >
        放弃更改
      </el-button>
      <span style="float: right">
        <el-button type="danger" size="mini" @click="batchRemove()">
          删除选中
        </el-button>
      </span>
    </div>
    <el-tree
      :data="data"
      :props="defaultProps"
      ref="menuTree"
      show-checkbox
      node-key="catId"
      :draggable="draggable"
      :expand-on-click-node="false"
      :allow-drop="allowDrop"
      @node-drop="handleDrop"
      :default-expanded-keys="expandedKeys"
    >
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>{{ node.label }}</span>
        <span>
          <el-button
            v-if="node.level <= 2"
            type="text"
            size="mini"
            @click="() => append(data)"
          >
            添加子节点
          </el-button>
          <el-button type="text" size="mini" @click="() => edit(data)">
            修改
          </el-button>
          <el-button
            v-if="node.isLeaf"
            type="text"
            size="mini"
            @click="() => remove(node, data)"
          >
            删除
          </el-button>
        </span>
      </span>
    </el-tree>

    <el-dialog
      :title="dialogTitle"
      :close-on-click-modal="false"
      :visible.sync="dialogFormVisible"
    >
      <el-form :model="category" :rules="rules" ref="categoryForm">
        <el-form-item prop="name" label="活动名称" label-width="120px">
          <el-input v-model="category.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="选择图标" label-width="120px">
          <el-popover
            ref="iconListPopover"
            placement="bottom-start"
            trigger="click"
            popper-class="mod-menu__icon-popover"
          >
            <div class="mod-menu__icon-inner">
              <div class="mod-menu__icon-list">
                <el-button
                  v-for="(item, index) in iconList"
                  :key="index"
                  @click="iconActiveHandle(item)"
                  :class="{ 'is-active': item === category.icon }"
                >
                  <icon-svg :name="item"></icon-svg>
                </el-button>
              </div>
            </div>
          </el-popover>
          <el-input
            v-model="category.icon"
            v-popover:iconListPopover
            placeholder="菜单图标名称"
            :readonly="false"
            class="icon-list__input"
          ></el-input>
        </el-form-item>
        <el-form-item label="计量单位" label-width="120px">
          <el-input
            v-model="category.productUnit"
            autocomplete="off"
          ></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button
          :disabled="addDisabled"
          type="primary"
          @click="submitCategory('categoryForm')"
        >
          确 定
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import Icon from '@/icons'

export default {
  data() {
    return {
      data: [],
      iconList: [],
      expandedKeys: [],
      updateNodes: [],
      dialogFormVisible: false,
      dialogType: '',
      dialogTitle: '',
      addDisabled: false,
      draggable: false,
      maxLevel: 0,
      newParentCid: [],
      category: {
        catId: 0,
        name: '',
        parentCid: 0,
        catLevel: 1,
        showStatus: 1,
        sort: 0,
        icon: '',
        productUnit: '',
        productCount: 0
      },
      rules: {
        name: [
          { required: true, message: '请输入分类名称', trigger: 'blur' },
          {
            min: 1,
            max: 50,
            message: '长度在 1 到 50 个字符',
            trigger: 'change'
          }
        ]
      },
      defaultProps: {
        children: 'children',
        label: 'name'
      }
    }
  },

  methods: {
    batchRemove() {
      //拿到所有选中结点
      const checkedNodes = this.$refs.menuTree.getCheckedNodes()
      if (checkedNodes.length === 0) {
        this.$alert('未选择任何节点', '错误', {
          confirmButtonText: '确定',
          type: 'warning'
        })
        return
      }
      //拿出id和name集合
      const ids = checkedNodes.map(node => {
        return node.catId
      })
      let names = checkedNodes.map(node => {
        return node.name
      })

      let namesStr = '【'
      if (names.length >= 50) {
        for (let i = 0; i < names.length; i++) {
          if (i >= 50) {
            namesStr += names[i] + '】等'
            break
          }

          namesStr += names[i] + ','
        }
      } else {
        namesStr = names.toString() + '】'
      }

      this.$confirm(`是否批量删除${namesStr}菜单?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl('/product/category/delete'),
            method: 'delete',
            data: this.$http.adornData(ids, false)
          }).then(({ data }) => {
            if (data && data.code === 0) {
              this.$message({
                message: '批量删除成功',
                type: 'success'
              })
              //重新获取数据
              this.fetchData()
            } else {
              this.$message.error(data.msg)
            }
          })
        })
        .catch(() => {})
    },

    cancelUpdate() {
      this.updateNodes = []
      this.draggable = false
      //重新获取数据
      this.fetchData()
    },

    batchUpdateSort() {
      this.$http({
        url: this.$http.adornUrl(`/product/category/update/sort`),
        method: 'put',
        data: this.$http.adornData(this.updateNodes, false)
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.$message.success('菜单顺序调整成功')

          this.updateNodes = []
          //重新获取数据
          this.fetchData()
          //展开新增节点的父节点
          this.expandedKeys = this.newParentCid
          this.draggable = false
          this.maxLevel = 0
        } else {
          this.$message.error(data.msg)
        }
      })
    },

    handleDrop(draggingNode, dropNode, dropType, ev) {
      //找到新的父id，和层级关系
      let pCid
      let siblings = null
      //如果是移动到某个节点里面，那么这个节点的id就是他的新父id
      if (dropType === 'inner') {
        pCid = dropNode.data.catId
        siblings = dropNode.childNodes
      } else {
        //否则找到兄弟节点的父节点的id，就是他的新父id
        pCid = dropNode.parent.data.catId || 0
        siblings = dropNode.parent.childNodes
      }

      //根据层级关系排序
      for (let i = 0; i < siblings.length; i++) {
        const node = siblings[i]
        if (node.data.catId === draggingNode.data.catId) {
          //如果遍历到当前拖拽的节点
          let catLevel = draggingNode.level
          if (node.level !== catLevel) {
            //节点层级发生变化，更新数据
            catLevel = node.level
            //修改子节点的层级
            this.updateChildrenLevel(node)
          }

          //把父id更新了
          this.updateNodes.push({
            catId: node.data.catId,
            sort: i,
            parentCid: pCid,
            catLevel: catLevel
          })
        } else {
          this.updateNodes.push({
            catId: node.data.catId,
            sort: i
          })
        }
      }
      this.newParentCid.push(pCid)
      this.expandedKeys = [pCid]
    },

    updateChildrenLevel(node) {
      if (node.childNodes && node.childNodes.length > 0) {
        for (let i = 0; i < node.childNodes.length; i++) {
          const cNode = node.childNodes[i]
          this.updateNodes.push({
            catId: cNode.data.catId,
            catLevel: cNode.level
          })

          //递归更新当前节点的子节点
          this.updateChildrenLevel(cNode)
        }
      }
    },

    allowDrop(draggingNode, dropNode, type) {
      //计算出当前拖动节点的子节点的最大层数
      this.nodeChildrenMaxLevel(draggingNode)
      //拖动后节点的子节点有几级 = 最大层数 - 被拖动节点自身的等级
      //拖动后节点的层数 = 拖动后节点的子节点有几级 + 被拖动节点自身(1)
      let deep = this.maxLevel - draggingNode.level + 1
      //计算深度后重置最大层数
      this.maxLevel = 0

      if (type === 'inner') {
        //如果是移动到dropNode节点里面，dropNode的层数和被移动节点的深度相加要小于等于3
        return deep + dropNode.level <= 3
      } else {
        //如果是移动到dropNode节点同级，dropNode的父节点层数和被移动节点的深度相加要小于等于3
        return deep + dropNode.parent.level <= 3
      }
    },

    nodeChildrenMaxLevel(node) {
      //如果被拖动节点的catLevel等于3，由于3在当前业务已经是最大了一定没有子节点，所以直接给maxLevel赋值并返回
      if (node.level === 3) {
        this.maxLevel = 3
        return
      }

      //如果被拖动节点的catLevel不等于3，可能有子节点，有子节点进入
      if (node.childNodes && node.childNodes.length > 0) {
        for (let i = 0; i < node.childNodes.length; i++) {
          //由于3在当前业务已经是最大了，直接返回
          if (this.maxLevel === 3) {
            return
          }
          //找出最大的层级
          if (node.childNodes[i].level > this.maxLevel) {
            this.maxLevel = node.childNodes[i].level
          }
          //递归查询子节点的最大层级
          this.nodeChildrenMaxLevel(node.childNodes[i])
        }
      }
    },

    fetchData() {
      this.$http({
        url: this.$http.adornUrl('/product/category/list/tree'),
        method: 'get'
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.data = data.data
        } else {
          this.$message.error(data.msg)
        }
      })
    },

    append(data) {
      this.dialogType = 'add'
      this.dialogTitle = '添加子节点'
      this.category.parentCid = data.catId
      this.category.catLevel = data.catLevel + 1
      this.dialogFormVisible = true
    },

    edit(data) {
      this.dialogType = 'edit'
      this.$http({
        url: this.$http.adornUrl(`/product/category/info/${data.catId}`),
        method: 'get'
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.category = data.data
          this.dialogTitle = `修改节点【${this.category.name}】`
          this.dialogFormVisible = true
        } else {
          this.$message.error(data.msg)
        }
      })
    },

    submitCategory(formName) {
      //禁用提交按钮，防止重复提交
      this.addDisabled = true
      this.$refs[formName].validate(valid => {
        if (valid) {
          if (this.dialogType === 'add') {
            this.addCategory()
          } else if (this.dialogType === 'edit') {
            this.updateCategory()
          }
        }
      })
      this.addDisabled = false
    },

    updateCategory() {
      let { catId, name, icon, productUnit } = this.category

      this.$http({
        url: this.$http.adornUrl('/product/category/update'),
        method: 'put',
        data: this.$http.adornData({ catId, name, icon, productUnit }, false)
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.$message({
            message: '修改成功',
            type: 'success'
          })
          //重新获取数据
          this.fetchData()
          //展开新增节点的父节点
          this.expandedKeys = [this.category.parentCid]
          this.dialogFormVisible = false
        } else {
          this.$message.error(data.msg)
        }
      })
    },

    addCategory() {
      this.$http({
        url: this.$http.adornUrl('/product/category/save'),
        method: 'post',
        data: this.$http.adornData(this.category, false)
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.$message({
            message: '添加成功',
            type: 'success'
          })
          //重新获取数据
          this.fetchData()
          //展开新增节点的父节点
          this.expandedKeys = [this.category.parentCid]
          this.dialogFormVisible = false
        } else {
          this.$message.error(data.msg)
        }
      })
    },

    remove(node, data) {
      let ids = [data.catId]

      this.$confirm(`是否删除【${data.name}】菜单?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl('/product/category/delete'),
            method: 'delete',
            data: this.$http.adornData(ids, false)
          }).then(({ data }) => {
            if (data && data.code === 0) {
              this.$message({
                message: '删除成功',
                type: 'success'
              })
              //重新获取数据
              this.fetchData()
              //展开删除节点的父节点
              this.expandedKeys = [node.parent.data.catId]
            } else {
              this.$message.error(data.msg)
            }
          })
        })
        .catch(() => {})
    },

    // 图标选中
    iconActiveHandle(iconName) {
      this.category.icon = iconName
    }
  },

  created() {
    this.fetchData()
    this.iconList = Icon.getNameList()
  },

  watch: {
    //关闭弹出框时，清空category的数据
    dialogFormVisible(newVal) {
      if (!newVal) {
        this.category = {}
      }
    }
  }
}
</script>

<style scoped lang="scss">
.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}

.native-class {
  margin: 0 20px 20px 20px;
}

.mod-menu {
  .menu-list__input,
  .icon-list__input {
    > .el-input__inner {
      cursor: pointer;
    }
  }
  &__icon-popover {
    width: 458px;
    overflow: hidden;
  }
  &__icon-inner {
    width: 478px;
    max-height: 258px;
    overflow-x: hidden;
    overflow-y: auto;
  }
  &__icon-list {
    width: 458px;
    padding: 0;
    margin: -8px 0 0 -8px;
    > .el-button {
      padding: 8px;
      margin: 8px 0 0 8px;
      > span {
        display: inline-block;
        vertical-align: middle;
        width: 18px;
        height: 18px;
        font-size: 18px;
      }
    }
  }
  .icon-list__tips {
    font-size: 18px;
    text-align: center;
    color: #e6a23c;
    cursor: pointer;
  }
}
</style>
